package org.seckill.service.Impl;

import org.seckill.dao.SeckillDao;
import org.seckill.dao.SuccessKlledDao;
import org.seckill.dto.Exposer;
import org.seckill.dto.SeckillExecution;
import org.seckill.entity.Seckill;
import org.seckill.entity.SuccessKilled;
import org.seckill.enums.SeckillStaEnum;
import org.seckill.exception.RepeatKillException;
import org.seckill.exception.SeckillCloseException;
import org.seckill.exception.SeckillException;
import org.seckill.service.SeckillService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.util.Date;
import java.util.List;

/**
 * Created by Administrator on 2016/7/26.
 */
//@Component @Service @Dao @Controller

@Service
public class SeckillServiceImpl implements SeckillService{

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    //md5盐值字符串，用于混淆md5
  private final String slat="sdfsdf%%FD5sd5fsd5f5%%FDfsdfsdf";

    //注入Service依赖
    @Autowired    //j2ee  @Resource  @Inject
    private SeckillDao seckillDao;

    //注入Service依赖
    @Autowired
    private SuccessKlledDao successKlledDao;

    @Override
    public List<Seckill> getSeckillList() {
        return seckillDao.queryAll(0,4);
    }

    @Override
    public Seckill getById(long seckillId) {
        return seckillDao.queryById(seckillId);
    }

    @Override
    public Exposer exportSeckillUrl(long seckillId) {

        Seckill seckill = seckillDao.queryById(seckillId);

        if(seckill == null){

            return new Exposer(false,seckillId);
        }
        Date startTime = seckill.getStartTime();
        Date endTime = seckill.getEndTime();

        Date nowTime = new Date();
        if(nowTime.getTime() < startTime.getTime() || nowTime.getTime() > endTime.getTime() ){

            return new Exposer(false,seckillId,nowTime.getTime(),startTime.getTime(),
                    endTime.getTime());
        }

        //转化特定字符串的过程，不可逆
        String md5 =getMD5(seckillId);

        return new Exposer(true,md5,seckillId);
    }

    //生成md5
    private String getMD5(long seckillId){

        String base = seckillId + "/" + slat;
        String md5 = DigestUtils.md5DigestAsHex(base.getBytes());

        return md5;
    }

    @Override
    @Transactional
    /**
     * 使用注解控制事务方法的优点
     * 1:开发团队达成一致约定，明确标注事务方法的编程风格
     * 2.保证事物方法的执行时间尽可能短，不要穿插其他网络操作RPC/HTTP
     *    如果有剥离到事务外部去
     * 3.不是所有的方法都需要事务，如只有一条修操作，只读操作不需要事务控制
     */
    public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5)
            throws SeckillException, RepeatKillException, SeckillCloseException {
        if (md5 == null || !md5.equals(getMD5(seckillId))){

            throw new SeckillException("seckill data rewrite");

        }
        //执行秒杀逻辑：减库存 + 记录购买行为
        Date nowTime=new Date();
        int updateCount = seckillDao.reduceNumber(seckillId,nowTime);

        if(updateCount<=0){

            //没有更新到记录，秒杀结束
            throw new SeckillCloseException("seckill is closed");
        }else{
          try{

              //记录购买行为
              int insertCount = successKlledDao.insertSuccessKilled(seckillId,userPhone);
              if (insertCount<=0){
                  //重复秒杀
                  throw new RepeatKillException("seckill repeated");
              }else{
                  //秒杀成功
                  SuccessKilled successKilled=successKlledDao.queryByIdWithSeckill(seckillId,userPhone);
                  return new SeckillExecution(seckillId, SeckillStaEnum.SUCCESS,successKilled);

              }



          } catch (SeckillCloseException e2){

              throw e2;

          }catch (RepeatKillException e1){

              throw e1;

          }catch (Exception e){

              logger.error(e.getMessage());
              //所有编译期异常，转化为运行时异常
              throw new SeckillException("seckill inner error:" + e.getMessage());

          }

        }

    }
}
